﻿//////////////////////////////////////////////////////////////////////////////
//
// Copyright © 1998-2024 Glodon Company Limited.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the “Software”),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
//////////////////////////////////////////////////////////////////////////////
#include "CmdBooleanWall.h"
#include "CommandIds.h"
#include "CommandRegister.h"
#include "IMesh.h"
#include "IBody.h"
#include "AlgorithmMeshOperate.h"
#include "UiDocumentViewUtils.h"
#include "IUserTransaction.h"
#include "GcmpBuiltInCategoryUniIdentities.h"
#include "IGraphicsMeshBody.h"
#include "UiCommonDialog.h"
#include "IPickFilter.h"
#include "IPickTarget.h"
#include "InteractivePicker.h"
#include "IGraphicsNodeReference.h"
#include "IElementModelShape.h"
#include "IElementBasicInformation.h"
#include "IDocument.h"
#include "IGraphicsElementShape.h"
#include "IGenericElement.h"
#include "CategoryDefine.h"
#include "IGraphicsBRepBody.h"
#include "EnableCompileWarning_The_LAST_IncludeInCpp.h" 


using namespace Sample;
using namespace gcmp;

REGISTER_COMMAND(CmdBooleanWall)

CmdBooleanWall::CmdBooleanWall() : CommandBase(ID_CMD_BOOLEAN_WALL)
{

}

CmdBooleanWall::~CmdBooleanWall()
{

}

bool CmdBooleanWall::IsEnabled() const
{
    return UiDocumentViewUtils::GetCurrentDocument() != nullptr;
}

bool CmdBooleanWall::IsVisible() const
{
    return UiDocumentViewUtils::GetCurrentDocument() != nullptr;
}

namespace
{
    class PickMeshBodyFilter : public gcmp::IPickFilter
    {
    public:

        virtual bool AllowElement(const gcmp::ElementId& id) const override
        {
            IDocument* pDocument = UiDocumentViewUtils::GetCurrentDocument();
            DBG_WARN_AND_RETURN_FALSE_UNLESS(pDocument, L"pDocument为空", L"GDMP", L"2023-11-08");
            IGenericElement* pSelElement = quick_cast<IGenericElement>(pDocument->GetElement(id));
            if (pSelElement == nullptr)
                return false;
            IElementBasicInformation* pElementBasicInformation = pSelElement->GetBasicInformation();
            if (pElementBasicInformation == nullptr)
                return false;
            if (pElementBasicInformation->GetCategoryUid() != BuiltInCategoryUniIdentities::BICU_FORM)
                return false;
            return true;
        }

        virtual bool AllowGraphicsNode(const gcmp::IGraphicsNodeReference& nodeReference) const override
        {
            return true;
        }

        virtual bool SetPickTargetOption(gcmp::IPickTarget* pickTarget) override
        {
            DBG_WARN_AND_RETURN_FALSE_UNLESS(pickTarget, L"pickTarget为空", L"GDMP", L"2023-09-30");
            pickTarget->DisableAll();
            pickTarget->EnableGraphicsElementShape();
            return true;
        }
    };
    class PickWallFilter : public gcmp::IPickFilter
    {
    public:

        virtual bool AllowElement(const gcmp::ElementId& id) const override
        {
            IDocument* pDocument = UiDocumentViewUtils::GetCurrentDocument();
            DBG_WARN_AND_RETURN_FALSE_UNLESS(pDocument, L"pDocument为空", L"GDMP", L"2023-11-08");
            IGenericElement* pSelElement = quick_cast<IGenericElement>(pDocument->GetElement(id));
            if (pSelElement == nullptr)
                return false;
            IElementBasicInformation* pElementBasicInformation = pSelElement->GetBasicInformation();
            if (pElementBasicInformation == nullptr)
                return false;
            if (pElementBasicInformation->GetCategoryUid() != Sample::WALL_CATEGORY)
                return false;
            return true;
        }

        virtual bool AllowGraphicsNode(const gcmp::IGraphicsNodeReference& nodeReference) const override
        {
            return true;
        }

        virtual bool SetPickTargetOption(gcmp::IPickTarget* pickTarget) override
        {
            pickTarget->DisableAll();
            pickTarget->EnableGraphicsElementShape();
            return true;
        }
    };
}

gcmp::OwnerPtr<gcmp::IAction> CmdBooleanWall::ExecuteCommand(const gcmp::CommandParameters& cmdParams)
{
    IDocument* pDocument = UiDocumentViewUtils::GetCurrentDocument();
    DBG_WARN_AND_RETURN_NULLPTR_UNLESS(pDocument, L"pDocument为空", L"GDMP", L"2023-09-30");

    //1.拾取mesh对象
    bool isMultiSelect = false;
    const bool isPickPointSelection = true;
    std::wstring mouseTooltip = L"选择一个待扣减Mesh对象";
    std::wstring statusBarTooltip = L"选择一个待扣减Mesh对象";
    Vector3d mousePoint;
    GraphicsNodeReferenceOwnerPtrVector meshBodySelections;
    OwnerPtr<PickMeshBodyFilter> pickMeshBodyfilter(NEW_AS_OWNER_PTR(PickMeshBodyFilter));
    auto pickResult = InteractivePicker::Select(isMultiSelect, isPickPointSelection, TransferOwnership(pickMeshBodyfilter), mouseTooltip, statusBarTooltip, mousePoint, meshBodySelections);
    if (pickResult.FinishStatus != InteractivePicker::PickerFinishStatus::Successful || meshBodySelections.size() != 1)
        return nullptr;

    //2.拾取墙
    mouseTooltip = L"选择墙对象";
    statusBarTooltip = L"选择墙对象";
    GraphicsNodeReferenceOwnerPtrVector wallSelections;
    OwnerPtr<PickWallFilter> pickWallfilter(NEW_AS_OWNER_PTR(PickWallFilter));
    pickResult = InteractivePicker::Select(isMultiSelect, isPickPointSelection, TransferOwnership(pickWallfilter), mouseTooltip, statusBarTooltip, mousePoint, wallSelections);
    if (pickResult.FinishStatus != InteractivePicker::PickerFinishStatus::Successful || wallSelections.size() == 0)
        return nullptr;

    //3.获取mesh对象图形节点上的MeshBody
    auto&& meshBodyNodeReference = meshBodySelections[0];
    DBG_WARN_AND_RETURN_NULLPTR_UNLESS(meshBodyNodeReference, L"meshBodyNodeReference为空", L"GDMP", L"2023-09-30");
    IElement* pSelMeshBodyElement = pDocument->GetElement(meshBodyNodeReference->GetElementId());
    DBG_WARN_AND_RETURN_NULLPTR_UNLESS(pSelMeshBodyElement, L"pSelMeshBodyElement为空", L"GDMP", L"2023-09-30");
    gcmp::IElementModelShape* pMeshBodyModelShape = pSelMeshBodyElement->GetElementModelShape();
    DBG_WARN_AND_RETURN_NULLPTR_UNLESS(pMeshBodyModelShape, L"pMeshBodyModelShape为空", L"GDMP", L"2023-09-30");
    const gcmp::IGraphicsElementShape* pMeshBodyElementShape = pMeshBodyModelShape->GetGraphicsElementShape();
    DBG_WARN_AND_RETURN_NULLPTR_UNLESS(pMeshBodyElementShape, L"pMeshBodyElementShape为空", L"GDMP", L"2023-09-30");
    const IMesh* pSelMesh = nullptr;
    const std::vector<OwnerPtr<IGraphicsNode>>& nodes = pMeshBodyElementShape->GetChildren();
    for (auto&& graphicsNode : nodes)
    {
        if (const IGraphicsMeshBody* graphicsMeshBody = quick_cast<IGraphicsMeshBody>(graphicsNode.get()))
        {
            const IMesh* pMesh = graphicsMeshBody->GetMesh();
            if (pMesh == nullptr || !pMesh->IsClosed()) continue;//非闭合不支持布尔运算
            pSelMesh = pMesh;
            break;
        }
    }
    if (!pSelMesh)
        UiCommonDialog::ShowMessageBox(L"提示", L"Mesh对象未选中或Mesh对象非闭合。", (int)UiCommonDialog::ButtonType::Yes);
    //4.通过墙图形节点来获取BRepBody，再获取body，再通过AlgorithmMeshOperate::CreateMeshFromBrepBody(...)转化为mesh    
    OwnerPtr<IMesh> opSubtractMesh = nullptr;
    auto&& opNodeRefOwner = wallSelections[0];
    if (opNodeRefOwner == nullptr) return nullptr;
    IElement* pSelWallElement = pDocument->GetElement(opNodeRefOwner->GetElementId());
    if (pSelWallElement == nullptr) return nullptr;
    gcmp::IElementModelShape* pWallModelShape = pSelWallElement->GetElementModelShape();
    if (pWallModelShape == nullptr) return nullptr;
    const gcmp::IGraphicsElementShape* pWallElementShape = pWallModelShape->GetGraphicsElementShape();
    if (pWallElementShape == nullptr) return nullptr;
    const std::vector<OwnerPtr<IGraphicsNode>>& nodes1 = pWallElementShape->GetChildren();
    OwnerPtr<IMesh> opWallMesh = nullptr;
    double distanceEpsilon = 20;
    double angleEpsilon = 0.2;
    double edgeLengthRatio = 0.0;
    for (auto&& graphicsNode : nodes1)
    {
        if (const IGraphicsBRepBody* graphicsBRepBody = quick_cast<IGraphicsBRepBody>(graphicsNode.get()))
        {
            const IBody* pWallBody = graphicsBRepBody->GetGeometry();
            if (pWallBody == nullptr)continue;
            gcmp::MeshFromBrepBodyErrorCode errorCode = gcmp::AlgorithmMeshOperate::CreateMeshFromBrepBody(pWallBody, distanceEpsilon, angleEpsilon, edgeLengthRatio, gcmp::DiscreteEpsilonType::User, opWallMesh);
            if (opWallMesh && errorCode == gcmp::MeshFromBrepBodyErrorCode::Success && opWallMesh->IsClosed())//非闭合不支持布尔运算
                break;
        }
    }
    if (opWallMesh == nullptr)  return nullptr;

    //5.对获取的mesh进行布尔运算，进行对墙的扣减运算
    AlgorithmMeshOperate::MeshBoolSubtract(pSelMesh, opWallMesh.get(), opSubtractMesh);
    if (opSubtractMesh == nullptr)
    {
        return nullptr;
    }

    //6.显示结果
    OwnerPtr<IUserTransaction> opUserTransaction = IUserTransaction::Create(pDocument, GBMP_TR(L"扣减墙"));
    DBG_WARN_AND_RETURN_NULLPTR_UNLESS(opUserTransaction, L"opUserTransaction创建失败", L"GDMP", L"2023-09-30");
    gcmp::OwnerPtr<IGraphicsElementShape> opSubtractShape = IGraphicsElementShape::Create(gcmp::GraphicsRenderLayer::Model);
    DBG_WARN_AND_RETURN_NULLPTR_UNLESS(opSubtractShape, L"opSubtractShape为空", L"GDMP", L"2023-09-30");
    opSubtractShape->AddChild(IGraphicsMeshBody::Create(TransferOwnership(opSubtractMesh)));
    pMeshBodyModelShape->SetGraphicsElementShape(TransferOwnership(opSubtractShape));
    opUserTransaction->Commit();

    return nullptr;
}